#include <iostream>
#include <cassert>
#include <cstring>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string>
#include <cerrno>

void usage(std::string proc)
{
  std::cout << "usage:\n\t" << proc << " need server_ip server_port" << std::endl;
}

int main(int argc, char* argv[])
{
  if (argc != 3)
  {
    usage(argv[0]);
    return 0;
  }

  //1.创建套接字,打开网络文件
  int sock = socket(AF_INET, SOCK_DGRAM, 0);
  
  if (sock < 0)
  {
      perror("create socket");
      std::cout << "errno : " << errno << std::endl;
      return 1;
  }

  //客户端需要显式的bind端口号?
  //1.客户端必须也要有IP和PORT
  //2.但是,客户端不需要显式的bind,一旦显式的bind,就必须明确
  //client要和哪一个port进行关联,client指明了端口号,那么client端就一定能够使用吗?
  //因为client是一个主动的角色,它向server发送信息时,假设你给它绑定了端口号
  //那么在client会临时的使用这个端口号,client的进程结束之后
  //那么这个端口号会返还给操作系统,这个时候如果又有一个client使用系统随机分配端口的策略,随机到了你bind的端口
  //在这个client进程没有退出的情况下,你又打开了之前的那个client,那么这个端口号被其他的进程占有了,所以会导致现在启动的client,无法绑定端口号,从而出错
  //所以我们一般采用的client端口分配策略采用的是由操作系统来进行随机分配的
  //可以避免上面的问题
  //那么server为什么就没有上面的哪些问题呢?
  //server是一个被动的角色,他需要一直来接收server的数据,所以必须保证自己的端口号不变,而且它是不会退出的,所以就不会导致上面的那一些问题

  //2.你把信息发送给谁?
  struct sockaddr_in server; 
  memset(&server, 0, sizeof(server)); 
  server.sin_family = AF_INET;
  uint16_t port = atoi(argv[2]);
  server.sin_port = htons(port);
  server.sin_addr.s_addr = inet_addr(argv[1]);

  //3.使用服务
  while(1)
  {
    //a.数据从哪里来
    //这种写法并不好,cin如果遇见" ",会自动停止读取
    //std::string message;
    //std::cout << "请输入信息 : ";
    //std::cin >> message;
    
    //建议这么写
    std::cout << "my_shell$ ";
    char line[1024];
    fgets(line, sizeof(line), stdin);

    sendto(sock, line, strlen(line), 0, (struct sockaddr*)&server, sizeof(server));

    struct sockaddr_in tmp;
    memset(&tmp, 0, sizeof(tmp));
    socklen_t len = sizeof(tmp);

    char buffer[1024];
    //这里的tmp就是一个占位符,因为是我们自己输入的IP和PORT,所以不需要获取来自server的网络地址信息
     
    ssize_t ret = recvfrom(sock, buffer, sizeof(buffer) - 1, 0, (struct sockaddr*)&tmp, &len);
    if (ret > 0)
    {
      //在网络通信中,只有报文大小,或者是字节流中字节的个数
      //没有C/C++字符串这样的概念(我们以后会经常见到)
      buffer[ret] = '\0';
      std::cout << "server echo : "<< buffer << std::endl;
    }
    else 
    {
      if (errno == 0)
      {
        continue;
      }
      else 
      {
        perror("read");
        std::cout << "errno : " << errno << std::endl;
        assert(false); 
      }
    }
  }
    return 0;
}
